//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         
// FILE:            string.cpp: implementation of the string class.
// AUTHOR:          Vladimir Gumenuk
// DATE:            20-Mar-2008 - included into ukrop
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>
#include "xort.h"

#define STR_BUF_LEN  64

#ifdef BUILD_FOR_MAC
char* _itoa(int value, char* result, int base) {
    // check that the base if valid
    if (base < 2 || base > 36) { *result = '\0'; return result; }
	
    char* ptr = result, *ptr1 = result, tmp_char;
    int tmp_value;
	
    do {
        tmp_value = value;
        value /= base;
        *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
    } while ( value );
	
    // Apply negative sign
    if (tmp_value < 0) *ptr++ = '-';
    *ptr-- = '\0';
    while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
    }
    return result;
}

int _vscprintf(const char * format, va_list pargs) {
    int retval;
    va_list argcopy;
    va_copy(argcopy, pargs);
    retval = vsnprintf(NULL, 0, format, argcopy);
    va_end(argcopy);
    return retval;
}
#endif


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

string::string()
{
    m_pchData = 0;
    m_dwLength = 0;
}

string::string( const string& Var )
{
    m_pchData = 0;
    m_dwLength = 0;
    copy_string( Var.m_pchData );
}

string::string( char cInit )
{
    m_pchData = 0;
    m_dwLength = 0;
    assign_char( cInit );
}

string::string( short sInit )
{
    char szBuf[STR_BUF_LEN];
    m_pchData = 0;
    m_dwLength = 0;
    _itoa( sInit, szBuf, 10 );
    copy_string( szBuf );
}

string::string( int iInit )
{
    m_pchData = 0;
    m_dwLength = 0;
    char szBuf[STR_BUF_LEN];
    _itoa( iInit, szBuf, 10 );
    copy_string( szBuf );
}

string::string( float fInit )
{
    m_pchData = 0;
    m_dwLength = 0;
    char szBuf[STR_BUF_LEN];
    sprintf( szBuf, "%f", fInit );
    copy_string( szBuf );
}

string::string( const char *pszInit )
{
    m_pchData = 0;
    m_dwLength = 0;
    copy_string( pszInit );
}

string::~string() 
{
    clear();
}

void string::clear()
{
    if( m_pchData )
    {
        free( m_pchData );
        m_pchData = 0;
        m_dwLength = 0;
    }
}

string& string::operator= ( const string& Var )
{   
    if( this == &Var ) // avoid copy to itself
        return *this;
    copy_string( Var.m_pchData );
    return *this;
}

string& string::operator= ( int iVal )
{
    clear();
    char szBuf[STR_BUF_LEN];
    _itoa( iVal, szBuf, 10 );
    copy_string( szBuf );
    return *this;
}

string& string::operator= ( float fVal )
{
    clear();
    char szBuf[STR_BUF_LEN];
    sprintf( szBuf, "%f", fVal );
    copy_string( szBuf );
    return *this;
}

string& string::operator= ( char cVal )
{
    assign_char( cVal );
    return *this;
}

string& string::operator+= ( char cVal )
{
    char szBuffer[2];
    szBuffer[0] = cVal;
    szBuffer[1] = 0;
    plus_string( szBuffer );    
    return *this;
}

string& string::operator= ( short sVal )
{
    clear();
    char szBuf[STR_BUF_LEN];
    _itoa( sVal, szBuf, 10 );
    copy_string( szBuf );
    return *this;
}

string& string::operator= ( const char *pszVal )
{
    clear();
    copy_string( pszVal );
    return *this;
}

string& string::operator+= ( const char *pszVal )
{
    plus_string( pszVal );
    return *this;
}

dword string::Add( const char* lpszStr )
{
    return plus_string( lpszStr );
}

string& string::operator+ ( const string& Var )
{
    plus_string( Var.m_pchData );
    return *this;
}

string& string::operator+= ( const string& Var )
{
    plus_string( Var.m_pchData );
    return *this;
}

bool string::operator== ( const string& Var )
{
    if( m_pchData && Var.m_pchData ) {
        if( 0 == strcmp( m_pchData, Var.m_pchData ) ) 
            return true;
    }
    return false;
}

bool string::operator== ( const char *pszVal )
{
    if( m_pchData && pszVal ) {
        if( 0 == strcmp( m_pchData, pszVal ) ) 
            return true;
    }
    return false;
}

bool string::operator< ( const string& Var )
{
    if( m_pchData && Var.m_pchData ) {
        if( strcmp( m_pchData, Var.m_pchData ) < 0 ) 
            return true;
    }
    return false;
}

bool string::operator> ( const string& Var )
{
    if( m_pchData && Var.m_pchData ) {
        if( strcmp( m_pchData, Var.m_pchData ) > 0 ) 
            return true;
    }
    return false;
}

bool string::operator!= ( const string& Var )
{
    if( m_pchData && Var.m_pchData ) {
        if( strcmp( m_pchData, Var.m_pchData ) ) 
            return true;
    }
    return false;
}

bool string::operator!= ( const char *pszVar )
{
    if( m_pchData && pszVar ) {
        if( strcmp( m_pchData, pszVar ) ) 
            return true;
    }
    return false;
}

dword string::copy_string( const char *pszSrc )
{
    clear();
    if( pszSrc ) {
        m_dwLength = strlen( pszSrc );
        m_pchData = strdup( pszSrc );
    }
    return m_dwLength;
}

void string::assign_char( char ch )
{
    char szBuffer[2];
    szBuffer[0] = ch;
    szBuffer[1] = 0;
    copy_string( szBuffer );
}

dword string::plus_string( const char *pszSrc )
{
    dword dwLength = 0;
    if( pszSrc )
    {
        dword dwLenSrc = strlen( pszSrc );
        if( dwLenSrc > 0 )
        {
            if( m_pchData )
            {
                dwLenSrc += 1;
                dword dwLenDst = strlen( m_pchData )+1;
                dwLength = dwLenSrc + dwLenDst;
                m_pchData = (char *)realloc( m_pchData, dwLength );
                if( m_pchData ) {
                    strcat( m_pchData, pszSrc );
                } else {
                    dwLength = 0;
                }
            } else {
                m_pchData = strdup( pszSrc );
            }
        }
    }
    if( m_pchData )
        m_dwLength = strlen( m_pchData );
    
    return m_dwLength;
}

dword string::Append( char ch )
{
    this->operator+=( ch );
    return m_dwLength;
}

dword string::Assign( const char* lpszStr )
{
    clear();
    return copy_string( lpszStr );
}

void string::attach( const string& Var )
{
    clear();
    copy_string( Var.m_pchData );
}

void string::Attach( char* lpszStr )
{
    clear();
    m_pchData = lpszStr;
    m_dwLength = strlen( m_pchData );
}

char * string::Detach()
{
    char *pszStr = m_pchData;
    m_pchData = 0;
    m_dwLength = 0;
    return pszStr;
}

char string::CharAt( dword dwPos )
{
    if( m_pchData && strlen( m_pchData ) > dwPos )
        return m_pchData[dwPos];
    return 0;
}

int string::Compare( const char *pstr )
{
    if( m_pchData && pstr )
        return strcmp( m_pchData, pstr );
    return -1;
}

dword string::Length()
{
    return m_dwLength;
}

bool string::AllocBuffer( dword size )
{
    clear();
    m_pchData = (char *)malloc( size + 1 );
    if( m_pchData )
        m_pchData[0] = 0;

    return ( m_pchData ) ? true : false;
}

char * string::GetBuffer()
{
    return m_pchData;
}

double string::doubleValue()
{
    return atof( m_pchData );
}

float string::floatValue()
{
    return (float)atof( m_pchData );
}

int string::intValue()
{
    return atoi( m_pchData );
}

qlong string::longValue()
{
#ifdef BUILD_FOR_PC
    return _atoi64( m_pchData );
#else
    return a64l( m_pchData );
#endif
}

void string::double2Str( double d )
{
    char szBuf[STR_BUF_LEN];
    sprintf( (char*)&szBuf[0], "%.8f", d );
    clear();
    copy_string( szBuf );
}

void string::float2Str( float f )
{
    char szBuf[STR_BUF_LEN];
    sprintf( (char*)&szBuf[0], "%f", f );
    clear();
    copy_string( szBuf );
}

void string::dword2Str( dword d )
{
    char szBuf[STR_BUF_LEN];
    sprintf( (char*)&szBuf[0], "%u", d );
    clear();
    copy_string( szBuf );
}

void string::word2Str( word w )
{
    char szBuf[STR_BUF_LEN];
    sprintf( (char*)&szBuf[0], "%u", w );
    clear();
    copy_string( szBuf );
}

void string::byte2Str( byte b )
{
    char szBuf[STR_BUF_LEN];
    sprintf( (char*)&szBuf[0], "%u", b );
    clear();
    copy_string( szBuf );
}

dword string::hasChr( byte ch ) const
{
    if( m_pchData ) {
        for( dword i = 0; i < m_dwLength; i++ ) {
            if( m_pchData[i] == ch ) 
                return i+1;
        }
    }
    return 0;
}

void string::Format( const char *pszFmt, ... )
{
    if( pszFmt )
    {
        char szBuffer[4096];
        va_list argptr;
        va_start( argptr, pszFmt );
        vsnprintf( szBuffer, 4095, pszFmt, argptr ); // at last we do not crash - fuck! it crashes!!! how?? :)
        va_end( argptr );
        copy_string( szBuffer );
    }
}

void string::FormatOneText( const char *pszFmt, const char *pszText )
{
    if( pszFmt && pszText )
    {
        dword iLen = ( strlen( pszText ) + strlen( pszFmt ) ) * 2; // allocate memory 2 times larger
        char *pszBuf = (char *)malloc( iLen );
        if( pszBuf )
        {
            sprintf( pszBuf, pszFmt, pszText );
            copy_string( pszBuf );
            free( pszBuf );
        }
    }
}

void string::TrimRight( char c )
{
    if( m_pchData )
    {
        char *pChr = strrchr( m_pchData, c );
        if( pChr )
            *pChr = 0;
    }
}

bool string::Insert( const char *lpstrText, dword iAt )
{
    if( m_pchData )
    {
        dword iLen = Length() + strlen( lpstrText ) + 1;
        char *pTemp = (char *)malloc( iLen );
        if( !pTemp )
            return false;
        
        dword i = 0;
        while( i < iAt )
        {
            pTemp[i] = m_pchData[i];
            ++i;
        }
        
        strcpy( &pTemp[i], lpstrText );
        strcat( pTemp, &m_pchData[i] );

        free( m_pchData );
        m_pchData = pTemp;
    }
    else
    {
        copy_string( lpstrText );
    }

    return true;
}

dword string::Replace( const char *lpstrOld, const char *lpstrNew, dword dwNumReplacements /*= 0*/ )
{
    if( !lpstrOld || !m_pchData )
        return 0;

    dword nSourceLen = strlen( lpstrOld );
    if( nSourceLen == 0 )
        return 0;
    dword nReplacementLen = lpstrNew ? strlen( lpstrNew ) : 0;
    dword nCount = 0;
    dword nDataLength = strlen( m_pchData );
    char *lpszStart = m_pchData;
    char *lpszEnd = m_pchData + nDataLength;
    char *lpszTarget;

    // loop once to figure out the size of the result string
    while( lpszStart < lpszEnd )
    {
        while( (lpszTarget = strstr( lpszStart, lpstrOld )) != NULL )
        {
            ++nCount;
            lpszStart = lpszTarget + nSourceLen;
            if( dwNumReplacements == nCount )
                break;
        }
        lpszStart += strlen( lpszStart ) + 1;
        if( dwNumReplacements == nCount )
            break;
    }

    // if any changes were made, make them
    if( nCount > 0 )
    {
        // if the buffer is too small, just allocate a new buffer (slow but sure)
        dword nOldLength = nDataLength;
        dword nNewLength =  nOldLength + (nReplacementLen-nSourceLen)*nCount;
        if( nDataLength < nNewLength )
        {
            char *pTemp = (char *)malloc( nNewLength + 1 );
            memcpy( pTemp, m_pchData, nDataLength*sizeof(char) );
            free( m_pchData );
            m_pchData = pTemp;
        }
        // else, we just do it in-place
        lpszStart = m_pchData;
        lpszEnd = m_pchData + nDataLength;

        dword dwCount2 = 0;

        // loop again to actually do the work
        while( lpszStart < lpszEnd )
        {
            while( (lpszTarget = strstr( lpszStart, lpstrOld ) ) != NULL )
            {
                dwCount2++;
                int nBalance = nOldLength - (lpszTarget - m_pchData + nSourceLen);
                memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance*sizeof(char) );
                memcpy( lpszTarget, lpstrNew, nReplacementLen*sizeof(char) );
                lpszStart = lpszTarget + nReplacementLen;
                lpszStart[nBalance] = '\0';
                nOldLength += (nReplacementLen - nSourceLen);

                if( dwNumReplacements == dwCount2 )
                    break;
            }
            lpszStart += strlen( lpszStart ) + 1;
            if( dwNumReplacements == dwCount2 )
                break;
        }
        
        assert( m_pchData[nNewLength] == '\0' );
    }

    return nCount;
}

dword string::Cut( const char *lpstrWhat )
{
    if( !lpstrWhat || !m_pchData )
        return 0;

    dword iPos = 0;
    char *pszTemp = strstr( m_pchData, lpstrWhat );

    if( pszTemp ) // sequence matched!
    {
        dword iStrLen = strlen( m_pchData );
        dword iSegLen = strlen( lpstrWhat );
        char *pszSkip = pszTemp + iSegLen;
        memmove( pszTemp, pszSkip, strlen( pszSkip ) );
        m_pchData[iStrLen-iSegLen] = 0;
        iPos = (dword)(pszTemp - m_pchData);
    }
    return iPos;
}

int string::ReverseFind( char ch )
{
    if( !m_pchData )
        return -1;

    char *psz = strrchr( m_pchData, ch );
    return( (psz == NULL) ? -1 : int( psz - m_pchData ) );
}

string string::Substring( int iStartPos )
{
    string csRet;
    if( Length() <= (dword)iStartPos )
    {
        return csRet;
    }

    csRet = &m_pchData[iStartPos];
    return csRet;
}

void string::Truncate( dword dwStartPos )
{
    if( Length() <= dwStartPos )
    {
        return;
    }

    m_pchData[dwStartPos] = 0;
    m_dwLength = strlen( m_pchData );
}

void string::CopyToBuffer( char *pszBuf ) const
{
    strcpy( pszBuf, m_pchData );
}

unsigned long string::get_hashCode() const
{
    long ii = 0;
    const char *pp = m_pchData;

    while( *pp )
        ii = ii << 1 ^ *pp++;

    if( ii < 0 )
        ii = -ii;

    ii %= STR_HASH_MAX;

    return ii;
}

int __cdecl string::strcmp( const char* src, const char* dst )
{
    int ret = 0;
    while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
        ++src, ++dst;
 
    if ( ret < 0 )
        ret = -1 ;
    else if ( ret > 0 )
        ret = 1 ;
    
    return( ret );
}

char * __cdecl string::strrchr( const char *str, int ch )
{
    char *start = (char *)str;
    
    while( *str++ )  /* find end of string */
        ;
    /* search towards front */
    while( --str != start && *str != (char)ch )
        ;
    
    if( *str == (char)ch)   /* char found ? */
        return( (char *)str );
    
    return 0;
}

char * __cdecl string::strcpy( char * dst, const char * src )
{
        char * cp = dst;
        while( (*cp++ = *src++) )
                ;               /* Copy src over dst */
        return( dst );
}

dword __cdecl string::strlen( const char * str  )
{
    const char *eos = str;
    
    while( *eos++ ) ;
    
    return( (int)(eos - str - 1) );
}

char * __cdecl string::strstr( const char * str1, const char * str2 )
{
    char *cp = (char *) str1;
    char *s1, *s2;
    
    if( !*str2 )
        return( (char *)str1 );
    
    while( *cp )
    {
        s1 = cp;
        s2 = (char *) str2;
        
        while( *s1 && *s2 && !(*s1-*s2) )
            s1++, s2++;

        if( !*s2 )
            return ( cp );

        cp++;
    }
    return(NULL);
}

char * __cdecl string::strcat( char * dst, const char * src )
{
    char * cp = dst;
    
    while( *cp )
        cp++;                   /* find end of dst */
    
    while( (*cp++ = *src++) ) ;       /* Copy src to end of dst */
    
    return( dst );                  /* return dst */
}

//////////////////////////////////////////////////////////////////////////
// string Friends
//////////////////////////////////////////////////////////////////////////
string __cdecl operator+ ( const char *s1, const string& s2 )
{
    string s;
    if( s1 && s2 )
    {
        s = s1;
        s += s2;
    }
    return s;
}

string __cdecl operator+ ( const string& s1, const char *s2 )
{
    string s;
    s = s1;
    s += s2;
    return s;
}

string __cdecl operator+ ( const string& s1, const string& s2 )
{
    string s = s1;
    s += s2;
    return s;
}

string __cdecl operator+ ( const string& s1, int iVal )
{
    string i( iVal );
    string s = s1;
    s += i;
    return s;
}

string __cdecl operator+ ( const string& s1, float fVal )
{
    string f( fVal );
    string s = s1;
    s += f;
    return s;
}

